#pragma once

#include <iostream>

#include <memory>

#include <sys/epoll.h>

#include "TcpSocket.hpp"

#include "log.hpp"

#include "nocopy.hpp"

#include "Epoller.hpp"

const uint32_t EVENT_IN = EPOLLIN;
const uint32_t EVENT_OUT = EPOLLOUT;
const uint32_t EVENT_DEL_OP = 0;

class EpollServer : public nocopy
{
    static const int _num = 64;
    // 表示用户预设就绪事件空间单次最大读取就绪事件数量

public:
    EpollServer(uint16_t port) // 此处使用智能指针 因此在初始化列表中使用 new 实例化
        : _port(port), _listensocket(new NetSocket), _epoller(new Epoller)
    {
    }

    void Init()
    { // 正常的创建 绑定 监听三件套
        _listensocket->Socket();
        _listensocket->Bind(_port);
        _listensocket->Listen();

        lg(INFO, "Create listen socket sucess, fd: %d", _listensocket->GetFd());
    }

    void Start()
    {
        // 在进行循环前 第一次调用必须保证监听套接字被添加至epoll当中
        // 这里本质是将监听套接字与其所关心的事件添加至内核epoll模型的红黑树当中
        _epoller->EpollerUpdate(EPOLL_CTL_ADD, _listensocket->GetFd(), EVENT_IN);

        struct epoll_event revs[_num];
        for (;;) // 运行过程中采用循环
        {
            int n = _epoller->EpollerWait(revs, _num);
            /*
                n 为返回值多少个
                所传入的revs数组为输出型参数
                _num表示每次最多从就绪队列中取多少个
            */
            if (n > 0) // 表有事件就绪
            {
                // lg(DEBUG,"Event happend, fd: %d",revs[0].data.fd); // debug
                Dispatcher(revs, n); // 进行事件派发
            }
            else if (n == 0)
            {
                // 表示 timeout
                lg(INFO, "time out...");
            }
            else
            {
                // 表示调用失败
                lg(WARNING, "EpollerWait error...");
            }
        }
    }

    void Accepter()
    {
        std::string clientip;
        uint16_t clientport;
        int newfd = _listensocket->Accept(&clientip, &clientport);
        if (newfd < 0)
        {
            lg(WARNING, "New fd Accept error: %s", strerror(errno));
        }
        lg(INFO, "New fd Accept Sucess, fd: %d", newfd);

        _epoller->EpollerUpdate(EPOLL_CTL_ADD, newfd, EVENT_IN);
    }

    void Recver(int fd)
    {
        char inbuff[1024];
        int n = read(fd, inbuff, sizeof(inbuff) - 1);
        if (n > 0)
        {
            inbuff[n] = 0;
            printf("Fd %d Get a message: %s", fd, inbuff);
            std::string echo_str = "Server Echo @ ";
            echo_str += inbuff;
            write(fd, echo_str.c_str(), echo_str.size());
        }
        else if (n == 0)
        {
            printf("Fd %d Closed, Me too...\n", fd);
            _epoller->EpollerUpdate(EPOLL_CTL_DEL, fd, EVENT_DEL_OP); // 在进行删除操作时确保文件描述符为一个有效的文件描述符
            close(fd);
        }
        else
        {
            lg(WARNING, "Read error...\n");
            _epoller->EpollerUpdate(EPOLL_CTL_DEL, fd, EVENT_DEL_OP);
            close(fd);
        }
    }

    void Dispatcher(struct epoll_event revs[], int num) // 进行事件派发
    {
        for (int i = 0; i < num; ++i)
        {
            uint32_t events = revs[i].events; // 获取事件
            int fd = revs[i].data.fd;         // 获取文件描述符

            if (events & EVENT_IN) // 其他
            {
                if (fd == _listensocket->GetFd()) // 监听套接字读事件就绪
                {
                    // Accepter()
                    Accepter();
                }
                else // 其他读事件就绪
                {
                    // Recver()
                    Recver(fd);
                }
            }
            else if (events & EVENT_OUT) // 写事件
            {
                // 暂时不考虑
            }
            else // 其他
            {
                // 暂时不考虑
            }
        }
    }
    ~EpollServer()
    {
        // 析构函数关闭套接字 (内置封装)
        _listensocket->Close();
    }

private:
    std::shared_ptr<NetSocket> _listensocket; // 使用智能指针
    std::shared_ptr<Epoller> _epoller;
    uint16_t _port;
};